feat(loop): ci-fix — automated bounded fix attempts for failing PR CI (issue #96, part 1) - #153
Merged
Merged
Conversation
Driver exited ~08:56Z 2026-07-16 without committing. Snapshot taken during manual debris triage so the work can't be lost; needs review, tests, and completion before any PR. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… suite (issue #96) Completes the WIP snapshot from a killed driver: pr-ci-fix.sh and its wiring into loop-census.sh/loop-tick.sh/loop-daemon.sh/loop-event.sh (verdict precedence feedback > ci-fix > advance, shared #95 per-issue attempt budget, distinct pr-loop-driver-cifix-pr<N> transient unit) were already complete after rebasing onto main (#146-#151); this adds the test coverage that was still missing: - loop-census.test.sh: ci_fix_prs=N passthrough from pr-ci-fix.sh. - loop-tick.test.sh: ci-fix verdict selection, lowest-PR tie-break, ci-fix-beats-advance precedence, and its tick-record fields. - loop-ceilings.test.sh: ci-fix shares the per-issue attempt budget with advance/feedback, and the full three-way precedence (feedback > ci-fix > advance) end to end through loop-tick.sh. - loop-daemon.test.sh: driver_unit_name/verdict_from_unit_name for the ci-fix shape, verify_and_classify_post_exit pass-through, and a full driver-spawn integration scenario. - loop-event.test.sh: the CI-FIX prompt variant (in-flight guard label, claude-ci-addressed marker instructions, one-shot contract). - plan-gate.test.sh: fixtures updated for pr-ci-fix.sh's now-mandatory sibling-script presence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
robercano
approved these changes
Jul 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Part 1 of #96: gives the autonomous PR loop a third reaction to a
loop-authored, open PR — a failing CI check, with no pending human
feedback, now gets an automated fix attempt instead of sitting red
forever.
pr-ci-fix.sh(new): detects open, bot-authored PRs whose currenthead commit has a failing CI check (CheckRun or legacy StatusContext
shape — same dual parse as
merge-ready.sh'sdecide()), excluding:a PR that's also a
pr-feedback.shcandidate (precedence, seebelow); PRs labeled
needs-human(budget already exhausted) orclaude-ci-fixing(fix already in flight); and a head whose fix hasalready been posted (
<!-- claude-ci-addressed:<head_sha> -->markercomment, keyed to the current head SHA so a genuinely new failure on
a new commit still re-triggers).
loop-census.sh: surfaces the count asci_fix_prs=N, folded intothe existing
cadence=FASTtrigger alongsidefeedback_prs.loop-tick.sh: wirespr-ci-fix.shin as step 5/5, parses its TSV(lowest-numbered PR wins, same tie-break as feedback), and emits
action=ci-fix pr=Nas a new verdict line.loop-event.sh: builds the CI-FIX driver prompt — labelclaude-ci-fixingfirst (in-flight guard), fix on the same branch(no new branch), push, then post the
claude-ci-addressedmarker withthe real head SHA. Never merges, never force-pushes.
loop-daemon.sh: gives ci-fix drivers their own transient systemdunit shape (
pr-loop-driver-cifix-pr<N>, distinct frompr-loop-driver-pr<N>for feedback) so a feedback driver and a ci-fixdriver on the same PR number can never collide in naming or reattach.
ci-fix pr=Nis excluded from the post-exit debris/phantom-branchverification (that check is for a freshly-created
feat/issue-N-*branch; ci-fix pushes onto an existing PR branch, so there's nothing
new to verify).
.claude/commands/pr-loop.md/.claude/self/pr-loop-self.md:STEP 0 tick contract updated to mention the new step and verdict.
Decision: verdict precedence
feedback > ci-fix > advance. Owner
CHANGES_REQUESTEDalwaysoutranks everything else — a human is waiting on a reply, so a PR that
is both red-CI and awaiting unaddressed feedback is handled entirely
as feedback (excluded from
pr-ci-fix.sh's own output, and enforcedagain at the verdict level in
loop-tick.sh— belt and suspenders).CI-fix outranks a fresh advance dispatch. Merge still requires a fresh
owner approval regardless of any of this —
merge-ready.shisuntouched.
Bounded attempts
CI-fix dispatches reuse the same per-issue attempt-budget counter
introduced in #95 (
loop-issue-attempts.json, keyed by the issue thePR's branch was cut from) — no new state file. Once the budget is
exhausted, the PR is labeled
needs-humanvia the existingneeds-human.shseam from #99, exactly like the advance/feedback paths.Out of scope (follow-ups)
Legs 2 (review-comment convergence) and 3 (conflict-after-sibling-merge
rebase) of #96 are not included here — separate follow-up PRs.
🤖 Generated with Claude Code